#include "D3D9HLSLProgram.h"
#include "FileDataStream.h"
#include "CommonFunction.h"
#include "RegisterData.h"

namespace DreamEngineD3D
{

CD3D9HLSLProgram::CD3D9HLSLProgram(CD3D9Device* pD3D9Device, ShaderProfile profile):
CGpuProgram(profile),
m_pD3D9Device(pD3D9Device)
{
}

CD3D9HLSLProgram::~CD3D9HLSLProgram()
{

}

Error CD3D9HLSLProgram::LoadFromFile(const CString& fileName, const CString& enterFun)
{
	CFileDataStream fileStr;
	fileStr.Open(fileName);
	CString strFragment;
	fileStr.GetAsString(strFragment);

	m_dwrd flags = D3DXSHADER_SKIPOPTIMIZATION;
	#if defined( DEBUG ) || defined( _DEBUG )
		flags |= D3DXSHADER_DEBUG;
	#endif

	if (D3DXCompileShader(strFragment.c_str(), strFragment.size(), 0, 0, enterFun.c_str(), 
		GetProfileAsString().c_str(), flags, 
		&m_pShaderBuffer, &m_pErrorBuffer, &m_pConstantTable)<0)
	{
		//E_FAIL

		if (m_pErrorBuffer != MATH_NULL)
		{
			MessageBox(0, (char*)m_pErrorBuffer->GetBufferPointer(), "Create CD3D9HLSLProgram", 0);
			m_pErrorBuffer->Release();
		}

		return ERROR_COMPILE_SHADER_ERROR;
	}

	fileStr.Close();

	return ERROR_OK;
}

Error CD3D9HLSLProgram::CreateParamTable(void)
{
	if (m_pConstantTable == MATH_NULL)
		return ERROR_HAVE_NULL_POINT;

	m_pShaderParamTable = new CShaderParamTable;

	D3DXCONSTANTTABLE_DESC desc;
	m_pConstantTable->GetDesc(&desc);

	for (m_uint32 i=0; i<desc.Constants; ++i)
	{
		D3DXHANDLE var = m_pConstantTable->GetConstant(0, i);
		D3DXCONSTANT_DESC desc; UINT decCount=0;
		m_pConstantTable->GetConstantDesc(var, &desc, &decCount);

		CString name(desc.Name); 
		CShaderParam::ParamType type = 
			CCommonFunction::D3D9ParamTypeToSPType(desc.Class, desc.Type, desc.Elements);
		m_uint8 elemCount		= desc.Elements;
		m_uint8 byteSize		= desc.Bytes;
		m_uint8 registerIdx	= desc.RegisterIndex;
		m_uint8 stMembers		= desc.StructMembers;
		
		m_pShaderParamTable->AddParam(name, type, elemCount, byteSize, registerIdx, stMembers);
	}

	CheckEngineError(m_pShaderParamTable->_BuildDataBuffer())

	return ERROR_OK;
}

void CD3D9HLSLProgram::UpdateAutoParams()
{
	m_pShaderParamTable->SetValue(FLAG_LINK_FLAG_STRING($, atp_worldViewProjMatrix),
								  CRegisterData::Instance().GetWorldViewProjMatrix());
	m_pShaderParamTable->SetValue(FLAG_LINK_FLAG_STRING($, atp_viewMatrix),
								  *CRegisterData::Instance().GetViewMatrix());
	m_pShaderParamTable->SetValue(FLAG_LINK_FLAG_STRING($, atp_worldMatrix),
								  *CRegisterData::Instance().GetWorldMatirx());
	m_pShaderParamTable->SetValue(FLAG_LINK_FLAG_STRING($, atp_cameraPos),
								  *CRegisterData::Instance().GetCameraPosition());
}

void CD3D9HLSLProgram::UpdateParams()
{
	CShaderParamTable::ParamIter paramIter = m_pShaderParamTable->GetParamIter();

	while (paramIter.HasMore())
	{
		CShaderParam* pParam = paramIter.Current(); 

		const CString& name = pParam->GetName();
		m_byte* pDatePtr = (m_byte*)(pParam->GetDataPtr());
		m_uint8 dataSize = pParam->GetSize();

		CShaderParam::ParamType type = paramIter.Current()->GetType();
		if (type == CShaderParam::SPT_TEXTURE)
		{
			IDirect3DTexture9* pTexture = MATH_NULL;
			pParam->GetDataVaule(&pTexture);
			m_pD3D9Device->GetD3DDevice()->SetTexture(pParam->GetRegisterIdx(), pTexture);
		}
		else
		{
			m_pConstantTable->SetValue(m_pD3D9Device->GetD3DDevice(),
				name.c_str(), pDatePtr, dataSize);
		}
		

		paramIter.Next();
	}
}

Error CD3D9HLSLFragment::CreateShader()
{
	if (m_pD3D9Device->GetD3DDevice()->CreatePixelShader(
		(m_uint32*)m_pShaderBuffer->GetBufferPointer(), &m_pShader) < 0)
	{
		return ERROR_CREATE_FRAGMENT_SHADER_FAILD;
	}
	
	return ERROR_OK;
}

CD3D9HLSLFragment::CD3D9HLSLFragment(CD3D9Device* pD3D9Device, ShaderProfile profile/*=SP_PS_2_0*/):
CD3D9HLSLProgram(pD3D9Device, profile),
m_pShader(MATH_NULL)
{
}


CD3D9HLSLFragment::~CD3D9HLSLFragment(void)
{
}



CD3D9HLSLVertex::CD3D9HLSLVertex(CD3D9Device* pD3D9Device, ShaderProfile profile/*=SP_VS_2_0*/):
CD3D9HLSLProgram(pD3D9Device, profile),
m_pShader(MATH_NULL)
{

}

CD3D9HLSLVertex::~CD3D9HLSLVertex(void)
{

}

Error CD3D9HLSLVertex::CreateShader()
{
	if (m_pD3D9Device->GetD3DDevice()->CreateVertexShader(
		(m_uint32*)m_pShaderBuffer->GetBufferPointer(), &m_pShader) < 0)
	{
		return ERROR_CREATE_VERTEX_SHADER_FAILD;
	}

	return ERROR_OK;
}

}
